//
//  MujiMediaAssetLoader.m
//  MujiCoreLibrary
//
//  Created by Matt Austin on 7/23/14.
//  Copyright (c) 2014 EnglishCentral, Inc. All rights reserved.
//

#import "MujiMediaAssetLoader.h"
#import "MujiMediaLoadRequest.h"
#import "MujiMediaViewAssetResourceLoaderDelegate.h"
#import "MujiMediaLoadRequestManager.h"
#import "NSError+MujiVideo.h"


#pragma mark -
#pragma mark Private Constants

static void *kMujiMediaAssetLoaderKVOContext = &kMujiMediaAssetLoaderKVOContext;

@interface MujiMediaAssetLoader () <MujiMediaClientProtocol, MujiMediaLoadRequestProtocol>

@property (readwrite, nonatomic, strong) MujiMediaLoadRequest *mediaRequest;
@property (readwrite, nonatomic, copy) AVURLAsset *urlAsset;
@property (readwrite, nonatomic, strong) AVPlayer *player;
@property (readwrite, nonatomic, strong) AVPlayerItem *playerItem;
@property (readwrite, nonatomic, assign, getter = isLoadingMediaAsset) BOOL loadingMediaAsset;
@property (readwrite, nonatomic, strong) NSError *error;
@property (readwrite, nonatomic, assign) NSUInteger timeoutCounter;
@property (readwrite, nonatomic, strong) NSTimer *timeout;

- (void)prepareToPlayAsset: (AVURLAsset *)asset withKeys:(NSArray *)requestedKeys;

@end



@implementation MujiMediaAssetLoader

#pragma mark -
#pragma mark Synthesizers

@synthesize mediaRequest            = _mediaRequest;
@synthesize player                  = _player;
@synthesize playerItem              = _playerItem;
@synthesize urlAsset                = _urlAsset;
@synthesize mediaAssetURL           = _mediaAssetURL;
@synthesize delegate                = _delegate;
@synthesize dataSource              = _dataSource;
@synthesize loadingMediaAsset       = _loadingMediaAsset;
@synthesize timeoutCounter          = _timeoutCounter;
@synthesize timeout                 = _timeout;


#pragma mark -
#pragma mark Getters


#pragma mark -
#pragma mark Setters

- (void)setTimeoutCounter:(NSUInteger)timeoutCounter
{
    if (_timeoutCounter != timeoutCounter)
    {
        [self willChangeValueForKey:NSStringFromSelector(@selector(timeoutCounter))];
        _timeoutCounter = timeoutCounter;
        [self didChangeValueForKey:NSStringFromSelector(@selector(timeoutCounter))];
    }
}

- (void)setPlayer:(AVPlayer *)player
{
    if (_player != player)
    {
        [self willChangeValueForKey:NSStringFromSelector(@selector(player))];
        
        NSArray * kvoKeyPaths = @[NSStringFromSelector(@selector(status)),
                                     NSStringFromSelector(@selector(rate))];
        for (NSString * kvoKeyPath in kvoKeyPaths)
        {
            @try
            {
                [_player removeObserver:self
                             forKeyPath:kvoKeyPath
                                context:kMujiMediaAssetLoaderKVOContext];
            }
            @catch (NSException *__unused exception) {}
        }
        
        _player = player;
        
        if (_player)
        {
            for (NSString * kvoKeyPath in kvoKeyPaths)
            {
                [_player addObserver:self
                          forKeyPath:kvoKeyPath
                             options:NSKeyValueObservingOptionNew
                             context:kMujiMediaAssetLoaderKVOContext];
            }
        }
        
        
        [self didChangeValueForKey:NSStringFromSelector(@selector(player))];
    }
}

- (void)setMediaRequest:(MujiMediaLoadRequest *)mediaRequest
{
    if (_mediaRequest != mediaRequest)
    {
        [self willChangeValueForKey:NSStringFromSelector(@selector(mediaRequest))];
        _mediaRequest = mediaRequest;
        [self didChangeValueForKey:NSStringFromSelector(@selector(mediaRequest))];
    }
}


- (void)setMediaAssetURL:(NSURL *)mediaAssetURL
{
    if (_mediaAssetURL != mediaAssetURL)
    {
        [self willChangeValueForKey:NSStringFromSelector(@selector(mediaAssetURL))];
        _mediaAssetURL = [mediaAssetURL copy];
        [self didChangeValueForKey:NSStringFromSelector(@selector(mediaAssetURL))];
    }
}



#pragma mark -
#pragma mark Initializers

- (id)init
{
    self = [super init];
    if (self)
    {
        _timeoutCounter          = 0;
    }
    return self;
}

#pragma mark -
#pragma mark NSObject

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    if (context == kMujiMediaAssetLoaderKVOContext)
    {
        if ([object isKindOfClass:[AVPlayer class]])
        {
            if ([keyPath isEqualToString:NSStringFromSelector(@selector(status))])
            {
                if ([self.delegate respondsToSelector:@selector(mediaAssetLoader:player:statusChange:)])
                {
                    [self.delegate mediaAssetLoader:self
                                             player:(AVPlayer *)object
                                       statusChange:[change copy]];
                }
            }
            else if ([keyPath isEqualToString:NSStringFromSelector(@selector(rate))])
            {
                if ([self.delegate respondsToSelector:@selector(mediaAssetLoader:player:rateChange:)])
                {
                    [self.delegate mediaAssetLoader:self
                                             player:(AVPlayer *)object
                                         rateChange:[change copy]];
                }
            }
        }
    }
    else
    {
        [super observeValueForKeyPath:keyPath
                             ofObject:object
                               change:change
                              context:context];
    }
}

- (void)dealloc
{
    NSArray * kvoKeyPaths = @[NSStringFromSelector(@selector(status)),
                              NSStringFromSelector(@selector(rate))];
    for (NSString * kvoKeyPath in kvoKeyPaths)
    {
        @try
        {
            [_player removeObserver:self
                         forKeyPath:kvoKeyPath
                            context:kMujiMediaAssetLoaderKVOContext];
            //_player = nil;
        }
        @catch (NSException *__unused exception) {}
    }
}

#pragma mark -
#pragma mark Public Methods

- (void)loadMediaAsset:(NSURL *)mediaAsset
{
    if (!mediaAsset)
    {
        self.error = [NSError errorWithCode:MujiVideoErrorCodeAssetFailedLoading];
        if ([[self delegate] respondsToSelector:@selector(mediaAssetLoader:failedLoadingWithError:)])
        {
            [self.delegate mediaAssetLoader:self failedLoadingWithError:self.error];
        }
        
        return;
    }
    
    MujiMediaLoadRequestManager *manager = [MujiMediaLoadRequestManager manager];
    
    self.mediaAssetURL           = mediaAsset;
    self.loadingMediaAsset       = YES;
    
    NSString *filename = [manager localFileForUrlString:[_mediaAssetURL absoluteString]];
    
    if (filename)
    {
        //NSLog(@"asdfg");
        
        // Update the date to reflect that this cached file has recently been used
        self.mediaRequest = [manager requestWithUrlString:[_mediaAssetURL absoluteString]];
        [manager setDate:[NSDate date] forRequest:_mediaRequest];
        
        NSURL *fileURL = [NSURL fileURLWithPath:filename];
        
        //NSLog(@"%@", filename);
        
        self.player    = [AVPlayer playerWithURL:fileURL];
        
        self.duration = CMTimeGetSeconds(self.player.currentItem.asset.duration);

        
    }
    else
    {
        // Here we use the iOS 7 loader delegate to read the asset data for us
        self.mediaRequest = [[MujiMediaLoadRequestManager manager] requestWithUrlString:[_mediaAssetURL absoluteString]];
        [manager setDate:[NSDate date] forRequest:self.mediaRequest];
        _mediaRequest.mediaClientDelegate = self;
        
        MujiMediaLoadRequest *mediaRequest = (MujiMediaLoadRequest *)self.mediaRequest;
        [mediaRequest loadAssetWithDelegate:self];
        
        self.playerItem = [AVPlayerItem playerItemWithAsset:mediaRequest.avAsset];
        self.player     = [AVPlayer playerWithPlayerItem:self.playerItem];
        
        //printf("%f", CMTimeGetSeconds(mediaRequest.avAsset.duration));
       // printf("%f", CMTimeGetSeconds(self.playerItem.asset.duration));

        
        NSURL    *movieURL = [NSURL URLWithString:[_mediaAssetURL absoluteString]];
        NSDictionary *opts = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:NO]
                                                         forKey:AVURLAssetPreferPreciseDurationAndTimingKey];
        AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:movieURL options:opts];  // 初始化视频媒体文件
        
        //int second = urlAsset.duration.value / urlAsset.duration.timescale;
        
        self.duration = CMTimeGetSeconds(urlAsset.duration);
        
//        printf("%f", CMTimeGetSeconds(urlAsset.duration));


    }
    
    self.player.actionAtItemEnd  = AVPlayerActionAtItemEndNone;
}

- (void)scheduleTimeoutWithInterval:(NSTimeInterval)interval
{
    [self cancelTimeout];
    
    self.timeout = [NSTimer timerWithTimeInterval:interval
                                           target:self
                                         selector:@selector(mediaAssetLoaderTimeoutTimerDidFire:)
                                         userInfo:nil
                                          repeats:NO];
    
    [[NSRunLoop currentRunLoop] addTimer:self.timeout
                                 forMode:NSDefaultRunLoopMode];
}

- (void)cancelTimeout
{
    if (self.timeout)
    {
        if (self.timeout.isValid)
        {
            [self.timeout invalidate];
        }
        
        self.timeout = nil;
    }
}



#pragma mark -
#pragma mark Private Methods

- (void)prepareToPlayAsset:(AVURLAsset *)asset withKeys:(NSArray *)requestedKeys
{
    self.error = nil;
    for (NSString *thisKey in requestedKeys)
    {
        NSError *error             = nil;
        AVKeyValueStatus keyStatus = [asset statusOfValueForKey:thisKey error:&error];
        if (keyStatus == AVKeyValueStatusFailed)
        {
            self.loadingMediaAsset = NO;
            self.timeoutCounter    = self.timeoutCounter + 1;
            if (error.code == NSURLErrorTimedOut)
            {
                
                
                self.error = [NSError errorWithCode:MujiVideoErrorCodeAssetPlaybackTimeout];
            }
            else
            {
                self.error = [NSError errorWithCode:MujiVideoErrorCodeAssetFailedLoading];
            }
            
            MujiMediaAssetLoader * __weak weakSelf = self;
            dispatch_async(dispatch_get_main_queue(), ^{
                MujiMediaAssetLoader * strongSelf = weakSelf;
                if ([strongSelf.delegate respondsToSelector:@selector(mediaAssetLoader:failedLoadingWithError:)])
                {
                    [strongSelf.delegate mediaAssetLoader:strongSelf failedLoadingWithError:strongSelf.error];
                }
            });

            return;
        }
    }
    
    if (!asset.isPlayable)
    {
        self.error = [NSError errorWithCode:MujiVideoErrorCodeAssetNotPlayable];
        
        MujiMediaAssetLoader * __weak weakSelf = self;
        dispatch_async(dispatch_get_main_queue(), ^{
            MujiMediaAssetLoader * strongSelf = weakSelf;
            if ([strongSelf.delegate respondsToSelector:@selector(mediaAssetLoader:failedLoadingWithError:)])
            {
                [strongSelf.delegate mediaAssetLoader:strongSelf failedLoadingWithError:strongSelf.error];
            }
        });
        
        return;
    }
    
    if (!self.playerItem)
    {
        self.playerItem = [AVPlayerItem playerItemWithAsset:asset];
    }
    
    if (!self.player)
    {
        self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
    }
    
    if (self.player.currentItem != self.playerItem)
    {
        [self.player replaceCurrentItemWithPlayerItem:self.playerItem];
    }
}

#pragma mark -
#pragma mark NSTimer

- (void)mediaAssetLoaderTimeoutTimerDidFire:(NSTimer *)timer
{
    if (timer == self.timeout)
    {
        if ([self.dataSource respondsToSelector:@selector(mediaAssetLoader:hasStartedPlaybackFromFiredTimeout:)])
        {
            if (![self.dataSource mediaAssetLoader:self hasStartedPlaybackFromFiredTimeout:timer])
            {
                if (!self.mediaRequest.downloadHasFinished)
                {
                    self.loadingMediaAsset = YES;
                    self.error             = [NSError errorWithCode:MujiVideoErrorCodeAssetPlaybackTimeout];
                    self.timeoutCounter    = self.timeoutCounter + 1;
                }
                else
                {
                    self.loadingMediaAsset = NO;
                    self.error             = [NSError errorWithCode:MujiVideoErrorCodeAssetFailedLoading];
                }
                
                if ([self.delegate respondsToSelector:@selector(mediaAssetLoader:failedLoadingWithError:)])
                {
                    [self.delegate mediaAssetLoader:self failedLoadingWithError:self.error];
                }
            }
        }
        
        [self cancelTimeout];
    }
}

#pragma mark -
#pragma mark MujiMediaClientProtocol

- (void)downloadCompleted:(MujiMediaLoadRequest *)mediaLoadRequest
{
    self.loadingMediaAsset = NO;
    if (!self.error)
    {
        if ([[self delegate] respondsToSelector:@selector(mediaAssetLoader:completedDownloadOfMediaLoadRequest:)])
        {
            [[self delegate] mediaAssetLoader:self completedDownloadOfMediaLoadRequest:mediaLoadRequest];
        }
    }
}

- (void)downloadFailed:(MujiMediaLoadRequest *)mediaLoadRequest
{
    self.loadingMediaAsset = NO;
    self.error             = [NSError errorWithCode:MujiVideoErrorCodeAssetFailedLoading];
    if ([[self delegate] respondsToSelector:@selector(mediaAssetLoader:failedLoadingWithError:)])
    {
        [self.delegate mediaAssetLoader:self failedLoadingWithError:self.error];
    }
}



@end
